using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;
using Konst3d.Efficiency.IO;

/* LAYOUT:
 * sign             /4b         0
 * version          /4b         4
 * reserved         /400b       8
 * days of week     /1b x7      408
 * days of month    /1b x31     415
*/

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class ActiveDaysStorage: BinaryFile, IActiveDaysStorage
    {
        #region Constants

        private const uint SIGNATURE = 0x02020202;
        private const uint VERSION = 1;
        private const int DAYS_OF_WEEK_OFFSET = 408;
        private const int DAYS_OF_MONTH_OFFSET = 415;

        #endregion // Constants

        #region Create/Open Methods

        public static ActiveDaysStorage Create(string path, bool overwrite)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));

            if (File.Exists(path) && !overwrite)
            {
                throw new IOException(String.Format("File \"{0}\" is already exists."));
            }

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, Encoding.UTF8);
                writer = new BinaryWriter(stream, Encoding.UTF8);

                writer.Write(SIGNATURE);
                writer.Write(VERSION);
                writer.WriteBytes(0, 400);
                writer.WriteBytes(1, 38);

                writer.Flush();
                writer.Seek(0, SeekOrigin.Begin);

                return new ActiveDaysStorage(stream, reader, writer);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        public static ActiveDaysStorage Open(string path)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(File.Exists(path));

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, Encoding.UTF8);
                writer = new BinaryWriter(stream, Encoding.UTF8);

                return new ActiveDaysStorage(stream, reader, writer);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        #endregion // Create/Open Methods

        #region Constructor

        private ActiveDaysStorage(FileStream stream, BinaryReader reader, BinaryWriter writer):
            base(stream, reader, writer, SIGNATURE, VERSION)
        {
            _week = _reader.ReadBytes(7, DAYS_OF_WEEK_OFFSET);
            _month = _reader.ReadBytes(31);
        }

        #endregion // Constructor

        #region IMonitoringExceptionsStorage Members

        public void UpdateDayOfWeek(int index, bool value)
        {
            byte byteValue = value ? (byte)1 : (byte)0;

            _writer.Write(byteValue, DAYS_OF_WEEK_OFFSET + index);
            _writer.Flush();

            _week[index] = byteValue;
        }

        public void UpdateDayOfMonth(int index, bool value)
        {
            byte byteValue = value ? (byte)1 : (byte)0;

            _writer.Write(byteValue, DAYS_OF_MONTH_OFFSET + index);
            _writer.Flush();

            _month[index] = byteValue;
        }

        public bool[] GetDaysOfWeek()
        {
            bool[] result = new bool[7];

            for (int i = 0; i < 7; ++i)
            {
                result[i] = _week[i] == 1;
            }

            return result;
        }

        public bool GetDayOfWeek(int index)
        {
            Contract.Requires(index >= 0 && index < 7);

            return _week[index] == 1;
        }

        public bool[] GetDaysOfMonth()
        {
            bool[] result = new bool[31];

            for (int i = 0; i < 31; ++i)
            {
                result[i] = _month[i] == 1;
            }

            return result;
        }

        public bool GetDayOfMonth(int index)
        {
            Contract.Requires(index >= 0 && index < 31);

            return _month[index] == 1;
        }

        #endregion

        #region Fields

        private byte[] _week;
        private byte[] _month;

        #endregion // Fields
    }
}
